package HashMap.Nodo.Operaciones;

import HashMap.Nodo.*;
import HashMap.GenerarCodigoNASM;
import HashMap.Nodo.Subclases.Escalar;
import HashMap.Pila;

public class OperacionesAritmeticas implements Operacion {

    private final Nodo nodo1;
    private final Nodo nodo2;
    private Nodo nodoFinal;
    private String operacion;

    public OperacionesAritmeticas(Nodo nodo1, Nodo nodo2) {
        this.nodo1 = nodo1;
        this.nodo2 = nodo2;
    }

    public OperacionesAritmeticas(Nodo nodo1) {
        this.nodo1 = nodo1;
        this.nodo2 = nodo1;
    }

    @Override
    public int getLongitud() {
        throw new UnsupportedOperationException("Operacion no soportada");
    }

    @Override
    public String generarCodigoNASM() {
        switch (operacion) {
            case "SUMA":
                return generarCodigoNASMSuma();
            case "RESTA":
                return generarCodigoNASMResta();
            case "MULTIPLICACION":
                return generarCodigoNASMMultiplicacion();
            case "DIVISION":
                return generarCodigoNASMDivision();
            case "UNARIO":
                return generarCodigoNASMUnario();
            default:
                return null;
        }
    }

    @Override
    public Nodo generarNodo(String tokenOperacion) {
        switch (tokenOperacion) {
            case "SUMA":
                return suma();
            case "RESTA":
                return resta();
            case "MULTIPLICACION":
                return multiplicacion();
            case "DIVISION":
                return division();
            case "UNARIO":
                return unario();
            default:
                return null;
        }
    }

    private boolean cumpleCondiciones() {
        if ((nodo1.getTipoBasicoDeDatos() == TipoBasicoDeDatos.INT || nodo1.getTipoBasicoDeDatos() == TipoBasicoDeDatos.FLOAT)
                && (nodo2.getTipoBasicoDeDatos() == TipoBasicoDeDatos.INT || nodo2.getTipoBasicoDeDatos() == TipoBasicoDeDatos.FLOAT)
                && (nodo1.getNivelDeIndireccion() == 0 || nodo1.getNivelDeIndireccion() == 1)
                && (nodo2.getNivelDeIndireccion() == 0 || nodo2.getNivelDeIndireccion() == 1)) {
            return true;
        }
        return false;
    }

    private TipoBasicoDeDatos tipoBasicoDeLaOperacion() {
        TipoBasicoDeDatos tipoBasico;
        if (nodo1.getTipoBasicoDeDatos() == TipoBasicoDeDatos.FLOAT || nodo2.getTipoBasicoDeDatos() == TipoBasicoDeDatos.FLOAT) {
            tipoBasico = TipoBasicoDeDatos.FLOAT;
        } else {
            tipoBasico = TipoBasicoDeDatos.INT;
        }
        return tipoBasico;
    }

    private Nodo rellenarNodoFinal() {
        TipoBasicoDeDatos tipoBasico = tipoBasicoDeLaOperacion();
        GenerarCodigoNASM generador = GenerarCodigoNASM.getInstancia();
        String nombre = generador.getNombreVariableAuxiliar();
        nodoFinal = new Escalar(nombre, tipoBasico, Categoria.VARIABLE, 0, operacion);
        Pila.getInstancia().addNodoALaTablaSuperior(nodoFinal);
        return nodoFinal;
    }

    private Nodo suma() {
        if (cumpleCondiciones()) {
            operacion = "SUMA";
            return rellenarNodoFinal();
        } else {
            throw new IllegalArgumentException("No se cumplen las condiciones "
                    + "para realizar la suma");
        }
    }

    private Nodo resta() {
        if (cumpleCondiciones()) {
            operacion = "RESTA";
            return rellenarNodoFinal();
        } else {
            throw new IllegalArgumentException("No se cumplen las condiciones "
                    + "para realizar la resta");
        }
    }

    private Nodo multiplicacion() {
        if (cumpleCondiciones()) {
            operacion = "MULTIPLICACION";
            return rellenarNodoFinal();
        } else {
            throw new IllegalArgumentException("No se cumplen las condiciones "
                    + "para realizar la multiplicacion");
        }
    }

    private Nodo division() {
        if (cumpleCondiciones()) {
            operacion = "DIVISION";
            return rellenarNodoFinal();
        } else {
            throw new IllegalArgumentException("No se cumplen las condiciones "
                    + "para realizar la division");
        }
    }

    private Nodo unario() {
        if (cumpleCondiciones()) {
            operacion = "UNARIO";
            return rellenarNodoFinal();
        } else {
            throw new IllegalArgumentException("No se cumplen las condiciones "
                    + "para realizar la division");
        }
    }

    private String generarCodigoNASMSuma() {
        return "add [" + nodo1.getNombre() + "], [" + nodo2.getNombre() + "]\n"
                + "mov [" + nodoFinal.getNombre() + "], aux";
    }

    private String generarCodigoNASMResta() {
        return "min [" + nodo1.getNombre() + "], [" + nodo2.getNombre() + "]\n"
                + "mov [" + nodoFinal.getNombre() + "], aux";
    }

    private String generarCodigoNASMMultiplicacion() {
        return "mult [" + nodo1.getNombre() + "], [" + nodo2.getNombre() + "]\n"
                + "mov [" + nodoFinal.getNombre() + "], aux";
    }

    private String generarCodigoNASMDivision() {
        return "div [" + nodo1.getNombre() + "], [" + nodo2.getNombre() + "]\n"
                + "mov [" + nodoFinal.getNombre() + "], aux";
    }

    private String generarCodigoNASMUnario() {
        return "not [" + nodo1.getNombre() + "]\n"
                + "mov [" + nodoFinal.getNombre() + "], aux";
    }
}
